/* * Author: Chris Seguin * * This software has been developed under the copyleft * rules of the GNU General Public License. Please * consult the GNU General Public License for more * details about use and distribution of this software. */ package org.acm.seguin.uml; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Point; import java.awt.Rectangle; import java.io.PrintWriter; import java.util.Iterator; import java.util.StringTokenizer; import javax.swing.JLabel; import javax.swing.JPanel; import org.acm.seguin.summary.FieldSummary; import org.acm.seguin.summary.MethodSummary; import org.acm.seguin.summary.PackageSummary; import org.acm.seguin.summary.Summary; import org.acm.seguin.summary.TypeSummary; import org.acm.seguin.uml.line.AssociationRelationship; import org.acm.seguin.uml.line.DragPanelAdapter; import org.acm.seguin.uml.line.EndPointPanel; import org.acm.seguin.uml.line.LabelSizeComputation; import org.acm.seguin.uml.line.SizableLabel; /** * Displays the summary of a type object * *@author Chris Seguin *@created June 7, 1999 */ public class UMLType extends EndPointPanel implements ISourceful { /** * Description of the Field */ protected int borderWidth = 2; /** * Description of the Field */ protected int lineSize = 0; // Instance Variables private UMLPackage parent; private SizableLabel nameLabel; private RoleHolder roles; private TypeSummary type; private int wide; private int high; private int titleHeight; private int state; // Colors private static Color defaultBG = null; private static Color selectedBG; private static Color foreignBG; private static Color selectedForeignBG; // States private final static int DEFAULT = 0; private final static int SELECTED = 1; private final static int FOREIGN = 2; private final static int TITLE_BORDER = 4; /** * Create a new instance of a UMLType * *@param initParent the parent *@param initType the initial type data *@param foreign Description of Parameter */ public UMLType(UMLPackage initParent, TypeSummary initType, boolean foreign) { super(null, true); // Remember local variables parent = initParent; type = initType; wide = 0; high = 0; if (foreign) { state = FOREIGN; } else { state = DEFAULT; } // Create a mouse listener UMLMouseAdapter listener = new UMLMouseAdapter(parent, this, null); addMouseListener(listener); // Create another adapter for draging this DragPanelAdapter adapter = new DragPanelAdapter(this, parent); addMouseListener(adapter); addMouseMotionListener(adapter); // Add the name label nameLabel = new SizableLabel(type.getName()); nameLabel.setLocation(borderWidth, borderWidth); nameLabel.setSLHorizontalAlignment(JLabel.CENTER); nameLabel.setSLFont(UMLLine.getProtectionFont(true, type.getModifiers())); Dimension titleSize = nameLabel.getPreferredSize(); titleHeight = titleSize.height; wide = titleSize.width + 2 * TITLE_BORDER; add(nameLabel); nameLabel.addMouseListener(listener); nameLabel.addMouseListener(adapter); nameLabel.addMouseMotionListener(adapter); // Check to see if we need a role roles = new RoleHolder(listener, adapter); if (type.isInterface()) { roles.add(((char) 171) + "Interface" + ((char) 187)); } if (foreign) { roles.add("Package: " + getPackageName()); } if (roles.hasAny()) { roles.setLocation(borderWidth, borderWidth + titleSize.height); add(roles); Dimension roleSize = roles.getPreferredSize(); roles.setSize(roleSize); wide = Math.max(wide, roleSize.width); titleHeight += roleSize.height; } // Determine the size of a line lineSize = computeLineSize(); // Add attribute labels int nY = titleHeight + borderWidth * 2; Iterator iter = type.getFields(); if (iter != null) { while (iter.hasNext()) { UMLField field = new UMLField(parent, this, (FieldSummary) iter.next(), adapter); field.setLocation(borderWidth, nY); add(field); lineSize = field.getPreferredSize().height; nY += field.getPreferredSize().height; wide = Math.max(wide, field.getPreferredSize().width); } } else { nY += lineSize; } // Add operation label nY += borderWidth; iter = type.getMethods(); if (iter != null) { while (iter.hasNext()) { MethodSummary nextMethod = (MethodSummary) iter.next(); if (!nextMethod.isInitializer()) { UMLMethod method = new UMLMethod(parent, this, nextMethod, adapter); method.setLocation(borderWidth, nY); add(method); nY += method.getPreferredSize().height; wide = Math.max(wide, method.getPreferredSize().width); } } } else { nY += lineSize; } // Add nested types label int nestedTypes = type.getTypeCount(); if (nestedTypes > 0) { nY += borderWidth; iter = type.getTypes(); if (iter != null) { while (iter.hasNext()) { UMLNestedType nestedType = new UMLNestedType(parent, this, (TypeSummary) iter.next(), adapter); nestedType.setLocation(borderWidth, nY); add(nestedType); nY += nestedType.getPreferredSize().height; wide = Math.max(wide, nestedType.getPreferredSize().width); } } } // Add the final extra space at the bottom high = nY + borderWidth; // Set the size nameLabel.setSize(wide, titleSize.height); if (roles.hasAny()) { roles.resetWidth(wide); } // Revise the width wide += (2 * borderWidth); // Set the size for the whole thing setSize(getPreferredSize()); } /** * Sets the Selected attribute of the UMLType object * *@param way The new Selected value */ public void setSelected(boolean way) { if (way) { select(); } else { deselect(); } } /** * Get the summary * *@return the summary */ public TypeSummary getSummary() { return type; } /** * Returns the minimum size * *@return The size */ public Dimension getMinimumSize() { return getPreferredSize(); } /** * Returns the preferred size * *@return The size */ public Dimension getPreferredSize() { return new Dimension(wide, high); } /** * Get the UML package that is holding this * *@return the package */ public UMLPackage getPackage() { return parent; } /** * Determine if this is selected * *@return true if this is selected */ public boolean isSelected() { return ((state & SELECTED) > 0); } /** * Determine if this is foreign * *@return true if this is foreign */ public boolean isForeign() { return ((state & FOREIGN) > 0); } /** * Return the background color * *@return the background color */ public Color getBackgroundColor() { if (defaultBG == null) { UMLType.initColors(); } if (state == SELECTED) { return selectedBG; } if (state == FOREIGN) { return foreignBG; } if (isSelected() && isForeign()) { return selectedForeignBG; } return defaultBG; } /** * Returns an identifier for a type * *@return an identifier for this panel */ public String getID() { return type.getPackageSummary().getName() + ":" + type.getName(); } /** * Count the number of attributes * *@param name Description of Parameter *@return the number of attributes */ public UMLField getField(String name) { if (name == null) { return null; } Component[] children = getComponents(); int last = children.length; for (int ndx = 0; ndx < last; ndx++) { if (children[ndx] instanceof UMLField) { UMLField field = (UMLField) children[ndx]; if (name.equals(field.getSummary().getName())) { return field; } } } return null; } /** * Paint this object * *@param g the graphics object */ public void paint(Graphics g) { // Set the background color Color bg = getBackgroundColor(); setBackground(bg); roles.setBackground(bg); // Paint the components super.paint(g); drawFrame(g, 0, 0); } /** * Print this object * *@param g the graphics object *@param x the x coordinate *@param y the y coordinate */ public void print(Graphics g, int x, int y) { // Set the background color Rectangle bounds = getBounds(); g.setColor(getBackgroundColor()); g.fillRect(x, y, bounds.width, bounds.height); // Draw the title Point pt = nameLabel.getLocation(); nameLabel.print(g, x + pt.x, y + pt.y); // Draw the role if (roles.hasAny()) { pt = roles.getLocation(); roles.print(g, x + pt.x, y + pt.y); } // Paint the components Component[] children = getComponents(); int last = children.length; for (int ndx = 0; ndx < last; ndx++) { if (children[ndx] instanceof UMLLine) { pt = children[ndx].getLocation(); ((UMLLine) children[ndx]).print(g, x + pt.x, y + pt.y); } } drawFrame(g, x, y); } /** * Resizes and repositions the compontents */ public void resize() { // Local Variables Component[] children = getComponents(); int last = children.length; // Set the default sizes wide = 0; high = 0; // Get the size of the title Dimension titleSize = nameLabel.getPreferredSize(); titleHeight = titleSize.height; wide = titleSize.width + 2 * borderWidth; if (roles.hasAny()) { Dimension roleSize = roles.getPreferredSize(); titleHeight += roleSize.height; wide = Math.max(roleSize.width, wide); } // Add attribute labels int nY = titleHeight + 2 * borderWidth; boolean foundField = false; for (int ndx = 0; ndx < last; ndx++) { if (children[ndx] instanceof UMLField) { UMLField field = (UMLField) children[ndx]; field.setLocation(borderWidth, nY); nY += lineSize; wide = Math.max(wide, field.getPreferredSize().width); foundField = true; } } if (!foundField) { nY += lineSize; } // Add operation label nY += borderWidth; boolean foundMethod = false; for (int ndx = 0; ndx < last; ndx++) { if (children[ndx] instanceof UMLMethod) { UMLMethod method = (UMLMethod) children[ndx]; method.setLocation(borderWidth, nY); nY += lineSize; wide = Math.max(wide, method.getPreferredSize().width); foundMethod = true; } } if (!foundMethod) { nY += lineSize; } // Add nested types label int nestedTypes = type.getTypeCount(); if (nestedTypes > 0) { nY += borderWidth; for (int ndx = 0; ndx < last; ndx++) { if (children[ndx] instanceof UMLNestedType) { UMLNestedType nestedType = (UMLNestedType) children[ndx]; nestedType.setLocation(borderWidth, nY); nY += lineSize; wide = Math.max(wide, nestedType.getPreferredSize().width); } } } // Add the final extra space at the bottom high = nY + borderWidth; // Set the size nameLabel.setSize(wide, titleSize.height); if (roles.hasAny()) { roles.resetWidth(wide); } // Revise the width wide += (2 * borderWidth); // Set the size for the whole thing setSize(getPreferredSize()); parent.repaint(); } /** * Select this item */ public void select() { state = state | SELECTED; repaint(); } /** * Select this item */ public void deselect() { state = state & ~SELECTED; repaint(); } /** * Toggle the selected state */ public void toggleSelect() { state = state ^ SELECTED; repaint(); } /** * Save the files * *@param output the output stream */ public void save(PrintWriter output) { Point pt = getUnscaledLocation(); output.println("P[" + getID() + "]{" + pt.x + "," + pt.y + "}"); } /** * Load the type * *@param buffer the buffer */ public void load(String buffer) { StringTokenizer tok = new StringTokenizer(buffer, ","); String strX = tok.nextToken(); String strY = tok.nextToken(); try { setLocation(Integer.parseInt(strX), Integer.parseInt(strY)); } catch (NumberFormatException nfe) { } } /** * Convert an attribute to an association * *@param packagePanel the package panel *@param fieldPanel the field panel *@return the new segmented line */ public AssociationRelationship convertToAssociation(UMLPackage packagePanel, UMLField fieldPanel) { remove(fieldPanel); resize(); packagePanel.add(fieldPanel); TypeSummary typeSummary = fieldPanel.getType(); JPanel endPanel = packagePanel.findType(typeSummary); if (endPanel == null) { endPanel = new UMLType(packagePanel, typeSummary, true); packagePanel.add(endPanel); endPanel.setLocation(0, 0); } AssociationRelationship result = new AssociationRelationship(this, (EndPointPanel) endPanel, fieldPanel); packagePanel.add(result); return result; } /** * Convert from an association to an attribute * *@param packagePanel the package panel *@param fieldPanel the field panel */ public void convertToAttribute(UMLPackage packagePanel, UMLField fieldPanel) { packagePanel.remove(fieldPanel); packagePanel.removeAssociation(fieldPanel); add(fieldPanel); resize(); } /** * Sets the scaling factor * *@param value scaling factor */ public void scale(double value) { super.scale(value); nameLabel.scale(value); roles.scale(value); // Rescale the children Component[] children = getComponents(); int last = children.length; for (int ndx = 0; ndx < last; ndx++) { if (children[ndx] instanceof UMLLine) { ((UMLLine) children[ndx]).scale(value); } } } /** * Returns the type summary for this class * *@return the type summary */ public Summary getSourceSummary() { return type; } /** * Get the name of the package * *@return the package name */ private String getPackageName() { Summary current = type; while (!(current instanceof PackageSummary)) { current = current.getParent(); } return ((PackageSummary) current).getName(); } /** * Count the number of attributes * *@return the number of attributes */ private int getAttributeCount() { int result = 0; Component[] children = getComponents(); int last = children.length; for (int ndx = 0; ndx < last; ndx++) { if (children[ndx] instanceof UMLField) { result++; } } return result; } /** * Draws the frame * *@param g the graphics object *@param x the x coordinate *@param y the y coordinate */ private void drawFrame(Graphics g, int x, int y) { g.setColor(UMLType.getFrameColor()); Dimension size = getSize(); double scalingFactor = getScale(); // Draw outer edge g.drawRect(x, y, size.width - 1, size.height - 1); g.drawRect(x + 1, y + 1, size.width - 3, size.height - 3); // Separate name from field g.drawLine(x, (int) (y + scalingFactor * (titleHeight + 4)), x + size.width - 1, (int) (y + scalingFactor * (titleHeight + 4))); g.drawLine(x, (int) (y + scalingFactor * (titleHeight + 5)), x + size.width - 1, (int) (y + scalingFactor * (titleHeight + 5))); // Separate field from methods int high = (int) (scalingFactor * (titleHeight + 4 + lineSize * Math.max(1, getAttributeCount()))); g.drawLine(x, y + high, x + size.width - 1, y + high); g.drawLine(x, y + high + 1, x + size.width - 1, y + high + 1); // Check if there are any nested types - if so draw their frame int typeCount = type.getTypeCount(); if (typeCount > 0) { int previousLabels = Math.max(1, getAttributeCount()) + Math.max(1, type.getMethodCount()); high = (int) (scalingFactor * (titleHeight + 4 + lineSize * previousLabels)); g.drawLine(x, y + high, x + size.width - 1, y + high); g.drawLine(x, y + high + 1, x + size.width - 1, y + high + 1); } } /** * Compute the line size * *@return Description of the Returned Value */ private int computeLineSize() { LabelSizeComputation lsc = LabelSizeComputation.get(); int height = lsc.computeHeight("Test", UMLLine.defaultFont); return height + 2 * UMLLine.labelMargin; } /** * Return the frame color * *@return the frame color */ private static Color getFrameColor() { return Color.black; } /** * Initializes the background colors for the various classes */ private static synchronized void initColors() { if (defaultBG == null) { defaultBG = Color.white; selectedBG = new Color(250, 255, 220); foreignBG = new Color(200, 200, 255); selectedForeignBG = new Color(220, 255, 220); } } }